const ratio = window.devicePixelRatio;

// 获取弧度
function getRadian(degrees) {
  return Math.PI / 180 * degrees;
}

// 获取弧度
function setProgress(progress, rate) {
  if (!rate) return 0;
  const radio = Math.abs(rate - progress);
  if (radio > 75) return 3;
  if (radio > 40) return 2;
  if (radio > 15) return 1;
  if (radio > 5) return 0.5;
  return 0.1;
}

export default class Board {
  constructor(option) {
    this.init(option);
  }

  init(option) {
    const { el, name, rate } = option;
    const { width, height } = el.parentNode.getBoundingClientRect();
    el.style.width = `${width}px`;
    el.style.height = `${height}px`;
    el.width = width * ratio;
    el.height = height * ratio;
    const ctx = el.getContext('2d');
    this.w = width * ratio; // 画布的宽度
    this.h = height * ratio; // 画布的高度
    this.x = width * ratio / 2;
    this.y = height * ratio * 3 / 5;
    this.startAngle = 160;
    this.endAngle = 380;
    this.segAngle = 40;
    this.stepAngle = () => (this.endAngle - this.startAngle) / this.segAngle;
    this.rate = rate;
    this.progress = 0;
    this.name = name;
    this.color = option.color;
    this.bgColor = option.bgColor;
    this.el = el;
    this.ctx = ctx;
    ctx.save();
    ctx.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
    ctx.restore();
    this.drawFrame();
  }

  // 获取圆边上点的坐标
  getRadiusPoint(radius, degrees) {
    return {
      x1: this.x + radius * Math.cos(degrees),
      y1: this.y + radius * Math.sin(degrees)
    };
  }

  // 画小圆点
  drawCircle(x, y, fillColor, mode = false) {
    const ctx = this.ctx;
    ctx.beginPath();
    ctx.arc(x, y, 0.7 * ratio, 0, 2 * Math.PI, mode);
    ctx.closePath();
    ctx.fillStyle = fillColor;
    ctx.fill();
  }

  // 画实线弧
  drawCircleLine({ color }, { radius }) {
    const { ctx, x, y, startAngle, endAngle } = this;
    ctx.beginPath();
    ctx.arc(x, y, radius, getRadian(startAngle), getRadian(endAngle));
    ctx.strokeStyle = color;
    ctx.lineCap = 'round';
    ctx.lineWidth = 4 * ratio;
    ctx.stroke();
  }

  // 画实线进度弧
  drawProgressLine({ color }, { radius, endAngle }) {
    const { ctx, x, y, startAngle } = this;
    ctx.beginPath();
    ctx.arc(x, y, radius, getRadian(startAngle), getRadian(endAngle));
    ctx.strokeStyle = color;
    ctx.lineCap = 'round';
    ctx.lineWidth = 4 * ratio;
    ctx.stroke();
  }

  // 画虚线圆弧
  drawCircleDashLine({ color }, { radius }) {
    const { startAngle, segAngle } = this;
    for (let i = 0; i <= segAngle; i += 1) {
      const { x1, y1 } = this.getRadiusPoint(radius, getRadian(startAngle + this.stepAngle() * i));
      this.drawCircle(x1, y1, color);
    }
  }

  // 绘制中心文本
  drawInnerText(x, y, text, fontSize = 30, color = '#fff', fontWeight = 'bold') {
    const ctx = this.ctx;
    ctx.save();
    ctx.fillStyle = color;
    ctx.font = `${fontWeight} ${fontSize * ratio}px -apple-system`;
    ctx.textAlign = 'center';
    ctx.fillText(`${text}`, x, y);
    ctx.textBaseline = 'ideographic';
    ctx.restore();
  }

  // 画当前点
  dotDrop(x = 60, y = 180, fillColor) {
    const ctx = this.ctx;
    ctx.beginPath();
    ctx.arc(x, y, ratio * 4, 0, 2 * Math.PI, false);
    ctx.fillStyle = fillColor;
    ctx.fill();
  }

  drawFrame() {
    const { x, y, w, h } = this;
    const { ctx, startAngle, endAngle, progress } = this;
    const { rate, name, color, bgColor } = this;
    // 清除画布内容
    ctx.clearRect(0, 0, w, h);
    // 填充背景色
    ctx.fillStyle = bgColor;
    ctx.fillRect(0, 0, w, h);
    this.progress += setProgress(progress, rate);
    // 绘制弧线
    this.drawCircleLine({ color: '#fff' }, { radius: x * 0.8 });
    this.drawCircleDashLine({ color }, { radius: x * 0.67 });
    // 文字位置
    this.drawInnerText(x, y - 5 * ratio, rate, 18, color);
    this.drawInnerText(x, y + 15 * ratio, name, 11, color);
    const { x1: sx, y1: sy } = this.getRadiusPoint(x * 0.8, getRadian(startAngle));
    const { x1: ex, y1: ey } = this.getRadiusPoint(x * 0.8, getRadian(endAngle));
    this.drawInnerText(sx, sy + 15 * ratio, '0', 12, color);
    this.drawInnerText(ex, ey + 15 * ratio, '100', 12, color);
    const curDeg = this.progress / 100 * (endAngle - startAngle) + startAngle;
    const { x1: cx, y1: cy } = this.getRadiusPoint(x * 0.8, getRadian(curDeg));
    this.dotDrop(cx, cy, color);
    this.drawProgressLine({ color }, { radius: x * 0.8, endAngle: curDeg });
    if (this.progress >= rate) return;
    window.requestAnimationFrame(() => {
      this.drawFrame();
    });
  }

  setRate(val) {
    this.progress = 0;
    this.rate = val;
    this.drawFrame();
  }
}
